<!--
 * @Description:
 * @Author bianpengfei
 * @create 2021/3/15 10:28
 -->
<template>
  <div class="g-nav-tag g-app__nav">
    <!--鼠标右击 弹出框-->
    <transition name="contextmenu" appear>
      <ul v-if="visible" :style="{ left: contextMenuLeft + 'px', top: contextMenuTop + 'px' }" class="contextmenu">
        <template v-for="(item, index) of _menuList">
          <template v-if="item.type == 'divide'">
            <div class="g-nav-contextmenu-divide" :key="index"></div>
          </template>
          <template v-else>
            <li @click.stop="handleTagsOption(item.type)" :key="index">
              <i :class="item.icon"></i>
              <span>{{ item.label }}</span>
            </li>
          </template>
        </template>
      </ul>
    </transition>
    <el-button class="left__icon" type="text" icon="el-icon-arrow-left" @click.stop="handleScroll('left')"></el-button>
    <div class="scroll-outer" ref="scrollOuter" @DOMMouseScroll="onMousewheelScroll" @mousewheel="onMousewheelScroll">
      <div class="scroll-body" ref="scrollBody" :style="{ transform: `translateX(${-tagLeft}px)` }">
        <draggable v-model="_navArr" @start="start" @end="end" :options="_draggableOptions">
          <div
            class="nav-tag__item"
            v-for="(item, index) in _navArr"
            :key="index + 1"
            ref="tagsPageOpened"
            :class="{
              'nav-tag__item--active': $route.fullPath == item.fullPath,
              'nav-tag__item--home': noCloseNavList.includes(item.name)
            }"
            @click="onClickTab(item, index)"
            @contextmenu.prevent.stop="contextMenu(item, index, $event)"
          >
            <i class="icon-dot"></i>
            <span class="nav-item__label">{{ item.title }}</span>
            <i v-if="!noCloseNavList.includes(item.name)" class="el-icon-close" @click.stop="removeTab(item)"></i>
          </div>
        </draggable>
      </div>
    </div>
    <div class="right__icon">
      <el-button type="text" icon="el-icon-arrow-right" @click.stop="handleScroll('right')"></el-button>
      <div class="dropdown__menu">
        <el-dropdown size="small">
          <span class="el-dropdown-link">
            <i class="el-icon-circle-close"></i>
          </span>
          <el-dropdown-menu slot="dropdown">
            <el-dropdown-item @click.stop.native="handleTagsOption('others')">关闭其他</el-dropdown-item>
          </el-dropdown-menu>
        </el-dropdown>
      </div>
    </div>
  </div>
</template>

<script>
import { sync, call, get } from 'vuex-pathify'
import { generateNavAttr } from '@bianpengfei/b-ui/lib/_util/router-cache'
import { throttleDtor } from '@bianpengfei/utils'
import Draggable from 'vuedraggable'

// 每次切换滚动的距离
const SCROLL_WIDTH = 250

export default {
  components: {
    Draggable
  },
  data() {
    return {
      active: 0,
      visible: false,
      tagLeft: 0,
      outerPadding: 4,
      scrollIndex: 0,
      contextMenuItem: {},
      contextMenuLeft: 0,
      contextMenuTop: 0,
      noCloseNavList: ['dashboard'] // 不允许关闭的nav
    }
  },
  computed: {
    _navArr: sync('app/navArr'),
    _navShow: get('app/navShow'),
    _isExternal: get('app/isExternal'),
    _menuDefaultActive: get('app/menuDefaultActive'),
    _menusList: get('app/menusList'),
    _layout: get('app/layout'),
    _navDraggable: get('app/navDraggable'),

    _draggableOptions() {
      return {
        preventOnFilter: false,
        delay: 50,
        sort: this._navDraggable,
        disabled: !this._navDraggable,
        animation: 150,
        ghostClass: 'nav-tag__item--ghost',
        dragClass: 'nav-tag__item--drag',
        bubbleScroll: true,
        forceFallback: true,
        removeCloneOnHide: false,
        scroll: true,
        ScrollSensitivity: 100,
        scrollSpeed: 100
      }
    },

    _menuList() {
      if (this.$route.fullPath == this.contextMenuItem.fullPath) {
        return [
          { type: 'refresh', label: '重新加载', icon: 'el-icon-refresh-left' },
          { type: 'divide' },
          { type: 'current', label: '关闭', icon: 'el-icon-circle-close' },
          { type: 'left', label: '关闭左边标签页', icon: 'el-icon-back' },
          { type: 'right', label: '关闭右边标签页', icon: 'el-icon-right' },
          { type: 'others', label: '关闭其他标签页', icon: 'el-icon-error' }
        ]
      }
      return [
        { type: 'current', label: '关闭', icon: 'el-icon-circle-close' },
        { type: 'refresh', label: '重新加载', icon: 'el-icon-refresh-left' },
        { type: 'divide' },
        { type: 'left', label: '关闭左边标签页', icon: 'el-icon-back' },
        { type: 'right', label: '关闭右边标签页', icon: 'el-icon-right' },
        { type: 'others', label: '关闭其他标签页', icon: 'el-icon-error' }
      ]
    }
  },

  watch: {
    $route: {
      handler(newVal) {
        // 从外部进入 不添加nav
        !this._isExternal &&
          this._addNavArrItem(
            generateNavAttr({
              ...newVal,
              title: this._menusList.find((v) => v.route == newVal.path)?.name || newVal.meta.title
            })
          )
        this.$nextTick(() => {
          this.init(newVal.path)
        })
      },
      immediate: true
    },

    visible(value) {
      if (value) {
        document.body.addEventListener('click', this.closeMenu)
      } else {
        document.body.removeEventListener('click', this.closeMenu)
      }
    }
  },

  mounted() {
    document.querySelector('body').addEventListener('click', this.onBodyClick, false)
    document.querySelector('body').addEventListener('mousemove', this.onMousemove, false)
    document.querySelector('body').addEventListener('contextmenu', this.onBodyClick, false)
    setTimeout(() => {
      this.init(this.$route.path)
    }, 200)
  },

  methods: {
    ...call('app', {
      _addNavArrItem: 'addNavArrItem',
      _removeNavItem: 'removeNavItem'
    }),

    init(path) {
      this.initPosition()
      this.scrollIndex = this._navArr.findIndex((item) => item.path == path) || 0
      this.moveToView(this.scrollIndex)
    },

    onBodyClick() {
      this.visible = false
    },

    @throttleDtor(100)
    onMousemove(originalEvent) {
      this._mouseEvent = originalEvent
      if (!this.navDraggableEnable || !this._navDraggable) return
      const startX = originalEvent.clientX
      const leftIconRect = this.$el.querySelector('.el-button.left__icon').getBoundingClientRect()
      const rightIconRect = this.$el.querySelector('.right__icon > .el-button').getBoundingClientRect()
      const dragEl = document.querySelector('.nav-tag__item--drag')
      if (dragEl?.nodeType == 1) {
        dragEl.style.display = 'none'
      }
      if (startX < leftIconRect.right + 30) {
        this.handleScroll('left')
        // dragEl.
      } else if (startX > rightIconRect.left - 30) {
        this.handleScroll('right')
      }
    },

    /*鼠标滚动tags*/
    onMousewheelScroll(e) {
      const type = e.type
      let delta = 0
      if (type === 'DOMMouseScroll' || type === 'mousewheel') {
        delta = e.wheelDelta ? e.wheelDelta : -(e.detail || 0) * 40
      }
      this.handleScroll(delta > 0 ? 'right' : 'left')
    },

    /**
     * 移除tab标签
     * @param data
     */
    removeTab(data) {
      if (this.noCloseNavList.includes(data.name)) return

      this._removeNavItem({
        flag: 'default',
        fullPath: data.fullPath
      })

      if (data.fullPath === this.$route.fullPath) {
        this.$nextTick(() => {
          const [route] = this._navArr.slice(-1)
          route ? this.$router.push(route) : this.$router.push('/')
        })
      }
    },

    handleTagsOption(type) {
      if (type == 'refresh') {
        const { fullPath } = this.contextMenuItem
        if (!this.$baseEventBus) return window.location.reload()
        if (fullPath == this.$route.fullPath) {
          // 当前页面
          this.$baseEventBus.$emit('reload-router-view')
        } else {
          // 非当前页面
          this.$baseEventBus.$emit('reload-router-view', this.contextMenuItem)
          this.$nextTick(() => {
            fullPath ? this.$router.push(fullPath) : this.$router.push('/')
          })
        }
        this.visible = false
        return
      }

      const map = new Map()
        .set('left', () => {
          this._removeNavItem({
            flag: 'left',
            ...this.contextMenuItem
          })
        })
        .set('right', () => {
          this._removeNavItem({
            flag: 'right',
            ...this.contextMenuItem
          })
        })
        .set('others', () => {
          this._removeNavItem({
            flag: 'others',
            ...this.contextMenuItem,
            fullPath: this.$route.fullPath
          })
        })
        .set('current', () => {
          this._removeNavItem({
            flag: 'default',
            ...this.contextMenuItem
          })
          if (this.$route.fullPath == this.contextMenuItem.fullPath) {
            this.$nextTick(() => {
              const [route] = this._navArr.slice(-1)
              route ? this.$router.push(route) : this.$router.push('/')
            })
          }
        })
      map.get(type)()
      this.visible = false
      setTimeout(() => {
        this.init(this.$route.fullPath)
      }, 100)
    },

    closeMenu() {
      this.visible = false
    },

    contextMenu(item, index, e) {
      // if (this.noCloseNavList.includes(item.name)) {
      //   return
      // }
      this.visible = false
      this.$nextTick(() => {
        this.contextMenuItem = {
          ...item,
          index
        }
        const offsetLeft = this.$el.getBoundingClientRect().left
        this.contextMenuLeft = e.clientX - offsetLeft + 10
        this.contextMenuTop = e.clientY - 55
        this.visible = true
      })
    },

    initPosition() {
      this.refsTag = this.$refs.tagsPageOpened
      let tempWidth = 0
      this.position = []
      this.scrollWidth = 0
      this.refsTag.forEach((item, index) => {
        if (item.nodeType == 1) {
          this.scrollWidth += item.offsetWidth + (index == 0 ? 0 : 5)
          this.position.push({
            width: item.offsetWidth,
            left: tempWidth
          })
          tempWidth += item.offsetWidth + (index == 0 ? 0 : 5)
        }
      })
    },

    handleScroll(type) {
      const outerWidth = this.$refs.scrollOuter.offsetWidth
      if (this.scrollWidth < outerWidth) return
      let scrollToLeft = Math.abs(this.tagLeft)
      if (type == 'left') {
        scrollToLeft = scrollToLeft - SCROLL_WIDTH > 0 ? scrollToLeft - SCROLL_WIDTH : 0
      } else if (type == 'right') {
        scrollToLeft = scrollToLeft + SCROLL_WIDTH
      }
      if (scrollToLeft > this.scrollWidth - outerWidth) {
        scrollToLeft = this.scrollWidth - outerWidth
      }
      this.tagLeft = scrollToLeft
    },

    moveToView(tag) {
      const outerWidth = this.$refs.scrollOuter.offsetWidth
      let scrollToLeft = Math.abs(this.tagLeft)
      const elToLeftWidth = this.position[~~tag]?.left
      if (this.scrollWidth > outerWidth) {
        const remainWidth = (elToLeftWidth - scrollToLeft) % outerWidth
        let realLeftWidth = 0
        // console.log('moveToView', remainWidth, realLeftWidth, outerWidth / 3)
        if ((elToLeftWidth - scrollToLeft) / outerWidth >= 1) {
          realLeftWidth = elToLeftWidth - scrollToLeft - remainWidth
        }
        if (remainWidth < outerWidth / 3) {
          // 点击左边三分之一
          scrollToLeft = realLeftWidth + (elToLeftWidth - SCROLL_WIDTH >= 0 ? elToLeftWidth - SCROLL_WIDTH : 0)
        } else if (remainWidth > ~~((outerWidth * 2) / 3)) {
          // 右边的三分之一
          scrollToLeft = realLeftWidth + (scrollToLeft == 0 ? elToLeftWidth : scrollToLeft + SCROLL_WIDTH)
        } else {
          scrollToLeft = elToLeftWidth
        }
        if (scrollToLeft > this.scrollWidth - outerWidth) {
          scrollToLeft = this.scrollWidth - outerWidth
        }
      } else {
        scrollToLeft = 0
      }
      this.scrollIndex = tag
      this.tagLeft = scrollToLeft
    },

    onClickTab(item, index) {
      this.$router.push(item.fullPath)
      this.moveToView(index)
    },

    start(evt) {
      this.navDraggableEnable = true
    },

    end(evt) {
      this.navDraggableEnable = false
      // console.log(evt, this._navArr[evt.newIndex])
      if (evt.newIndex == evt.oldIndex) return
      const fullPath = this._navArr[evt.newIndex]?.fullPath
      if (fullPath == this.$route.fullPath) {
        this.$nextTick(() => {
          this.init(fullPath)
        })
        return
      }
      this.$router.push(fullPath)
    }
  },
  beforeDestroy() {
    document.querySelector('body').removeEventListener('click', this.onBodyClick, false)
    document.querySelector('body').removeEventListener('contextmenu', this.onBodyClick, false)
  }
}
</script>

<style lang="scss">
/*缩放*/
.contextmenu-enter,
.contextmenu-leave-to {
  opacity: 0;
  transform: scale(0.9);
}
.contextmenu-enter-active {
  transition: transform 50ms ease, opacity 50ms ease;
}
.contextmenu-leave-active {
  transition: transform 50ms ease, opacity 50ms ease;
}
</style>
